﻿' ******************************************************************************
' ** 
' **  Yahoo! Managed
' **  Written by Marius Häusler 2011
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2011 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************
Imports System.Text.RegularExpressions
Imports MaasOne.YahooManaged.Finance


Namespace YahooManaged.Finance.NonAPI

    ''' <summary>
    ''' Provides methods for downloading key statistics of major companies.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class CompanyStatisticsDownload
        Inherits Base.StringDownload

        ''' <summary>
        ''' Raises if an asynchronous download of quotes data completes.
        ''' </summary>
        ''' <param name="sender">The event raising object</param>
        ''' <param name="ea">The event args of the asynchronous download</param>
        ''' <remarks></remarks>
        Public Event AsyncDownloadCompleted(ByVal sender As Base.Download, ByVal ea As CompanyStatisticsDownloadCompletedEventArgs)

        Friend ReadOnly mFinanceHelper As New FinanceHelper

        ''' <summary>
        ''' Downloads company statistic data.
        ''' </summary>
        ''' <param name="managedID">The managed ID</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Download(ByVal managedID As IID) As CompanyStatisticResponse
            If managedID Is Nothing Then Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Return Me.Download(managedID.ID)
        End Function
        ''' <summary>
        ''' Downloads company statistic data.
        ''' </summary>
        ''' <param name="unmanagedID">The unmanaged ID</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Download(ByVal unmanagedID As String) As CompanyStatisticResponse
            If unmanagedID = String.Empty Then Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Return Me.ToResponse(MyBase.DownloadString(Me.DownloadURL(unmanagedID)))
        End Function

        ''' <summary>
        ''' Starts an asynchronous download of company statistic data.
        ''' </summary>
        ''' <param name="managedID">The managed ID</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal managedID As IID, Optional ByVal userArgs As Object = Nothing)
            If managedID Is Nothing Then Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Me.DownloadAsync(managedID.ID, userArgs)
        End Sub
        ''' <summary>
        ''' Starts an asynchronous download of company statistic data.
        ''' </summary>
        ''' <param name="unmanagedID">The unmanaged ID</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal unmanagedID As String, Optional ByVal userArgs As Object = Nothing)
            If unmanagedID = String.Empty Then Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Dim args As New AsyncDownloadArgs(userArgs, unmanagedID)
            MyBase.DownloadStreamAsync(Me.DownloadURL(args.ID), args)
        End Sub

        ''' <summary>
        ''' Default constructor
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
        End Sub

        Private Sub DownloadAsync_Completed(ByVal sender As Base.Download, ByVal e As Base.StringDownloadCompletedEventArgs) Handles MyBase.AsyncStringDownloadCompleted
            If e IsNot Nothing AndAlso e.UserArgs IsNot Nothing AndAlso TypeOf e.UserArgs Is AsyncDownloadArgs Then
                Dim dlArgs As AsyncDownloadArgs = DirectCast(e.UserArgs, AsyncDownloadArgs)
                Dim args As New CompanyStatisticsDownloadCompletedEventArgs(dlArgs.UserArgs, Me.ToResponse(e.Response), dlArgs.ID)
                RaiseEvent AsyncDownloadCompleted(Me, args)
            End If
        End Sub

        Private Function DownloadURL(ByVal unmanagedID As String) As String
            Return "http://finance.yahoo.com/q/ks?s=" & unmanagedID & "+Key+Statistics"
        End Function

        Private Function ToResponse(ByVal resp As Base.StringResponse) As CompanyStatisticResponse
            Dim result As CompanyStatisticsData = Nothing

            Dim regRow As New Regex("<tr.*?>.*?</tr>")
            Dim regCell As New Regex("<td.*?>.*?</td>")

            Dim tableMatches As MatchCollection = Regex.Matches(resp.Result, "<table.*?>.*?</table>")
            If tableMatches.Count >= 18 Then
                Dim name As String = String.Empty
                Dim data As String = String.Empty
                Dim id As String = String.Empty

                Dim fiscalYearEnds, mostRecentQuarter, dividendDate, exDividendDate, lastSplitDate As Date
                Dim lastSplitFactor As SharesSplitFactor = Nothing
                Dim values1(8) As Double
                Dim values2(19) As Double
                Dim values3(22) As Double
                For tIndex As Integer = 0 To tableMatches.Count - 1

                    Dim rowMatches As MatchCollection = regRow.Matches(tableMatches(tIndex).Value)
                    If rowMatches.Count > 0 Then
                        For rIndex As Integer = 0 To rowMatches.Count - 1
                            Dim rvalue As String = rowMatches(rIndex).Value

                            Dim cellMatches As MatchCollection = regCell.Matches(rvalue)
                            If cellMatches.Count > 0 Then
                                For cIndex As Integer = 0 To cellMatches.Count - 1
                                    Dim value As String = mFinanceHelper.CleanTd(cellMatches(cIndex).Value)
                                    If value <> "N/A" Then
                                        Select Case tIndex
                                            Case 3
                                                If rIndex = 0 Then
                                                    Select Case cIndex
                                                        Case 0
                                                            Dim parts As String() = value.Split("("c)
                                                            name = parts(0)
                                                            If parts.Length > 1 Then
                                                                id = parts(1).Replace(")"c, "").ToUpper
                                                            End If
                                                        Case 1 : data = value
                                                    End Select
                                                End If

                                            Case 7
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 0, 1 : values1(rIndex) = mFinanceHelper.GetMillionValue(value)
                                                        Case 2, 3, 4, 5, 6, 7, 8 : values1(rIndex) = mFinanceHelper.ParseToDouble(value)
                                                    End Select
                                                End If
                                            Case 9
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1 : Date.TryParse(value, mFinanceHelper.DefaultYqlCulture, Globalization.DateTimeStyles.AdjustToUniversal, fiscalYearEnds)
                                                        Case 2 : Date.TryParse(value, mFinanceHelper.DefaultYqlCulture, Globalization.DateTimeStyles.AdjustToUniversal, mostRecentQuarter)
                                                    End Select
                                                End If

                                            Case 10
                                                If cIndex = 1 AndAlso (rIndex = 1 Or rIndex = 2) Then values2(rIndex - 1) = mFinanceHelper.ParseToDouble(value)

                                            Case 11
                                                If cIndex = 1 AndAlso (rIndex = 1 Or rIndex = 2) Then values2(rIndex - 1 + 2) = mFinanceHelper.ParseToDouble(value)

                                            Case 12
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1, 4, 5, 6 : values2(rIndex - 1 + 4) = mFinanceHelper.GetMillionValue(value)
                                                        Case 2, 3, 7, 8 : values2(rIndex - 1 + 4) = mFinanceHelper.ParseToDouble(value)
                                                    End Select
                                                End If

                                            Case 13
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1, 3 : values2(rIndex - 1 + 12) = mFinanceHelper.GetMillionValue(value)
                                                        Case 2, 4, 5, 6 : values2(rIndex - 1 + 12) = mFinanceHelper.ParseToDouble(value)
                                                    End Select
                                                End If

                                            Case 14
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1, 2 : values2(rIndex - 1 + 18) = mFinanceHelper.GetMillionValue(value)
                                                    End Select
                                                End If

                                            Case 16
                                                If cIndex = 1 And rIndex >= 1 And rIndex <= 7 Then values3(rIndex - 1) = mFinanceHelper.ParseToDouble(value)

                                            Case 17
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1, 2 : values3(rIndex - 1 + 7) = mFinanceHelper.ParseToDouble(value) / 1000
                                                        Case 3, 4, 7, 10 : values3(rIndex - 1 + 7) = mFinanceHelper.GetMillionValue(value)
                                                        Case 5, 6, 8, 9 : values3(rIndex - 1 + 7) = mFinanceHelper.ParseToDouble(value)
                                                    End Select
                                                End If

                                            Case 18
                                                If cIndex = 1 Then
                                                    Select Case rIndex
                                                        Case 1, 2, 3, 4, 5, 6 : values3(rIndex - 1 + 17) = mFinanceHelper.ParseToDouble(value)
                                                        Case 7 : Date.TryParse(value, mFinanceHelper.DefaultYqlCulture, Globalization.DateTimeStyles.AdjustToUniversal, dividendDate)
                                                        Case 8 : Date.TryParse(value, mFinanceHelper.DefaultYqlCulture, Globalization.DateTimeStyles.AdjustToUniversal, exDividendDate)
                                                        Case 9
                                                            Dim sp() As String = value.Split(":"c)
                                                            If sp.Length = 2 Then
                                                                Dim fromV, toV As Integer
                                                                If Integer.TryParse(sp(0), fromV) And Integer.TryParse(sp(1), toV) Then
                                                                    lastSplitFactor = New SharesSplitFactor(fromV, toV)
                                                                End If

                                                            End If
                                                        Case 10 : Date.TryParse(value, mFinanceHelper.DefaultYqlCulture, Globalization.DateTimeStyles.AdjustToUniversal, lastSplitDate)
                                                    End Select
                                                End If

                                        End Select
                                    End If
                                Next cIndex
                            End If
                        Next rIndex
                    End If
                Next tIndex

                result = New CompanyStatisticsData(name, id, data, _
                             New CompanyValuationMeasures(values1), _
                             New CompanyFinancialHighlights(fiscalYearEnds, mostRecentQuarter, values2), _
                             New CompanyTradingInfo(values3, dividendDate, exDividendDate, lastSplitDate, lastSplitFactor))
            End If
            Return New CompanyStatisticResponse(resp.Connection, result)
        End Function

        Private Class AsyncDownloadArgs
            Inherits Base.DownloadEventArgs
            Public ID As String
            Public Sub New(ByVal userArgs As Object, ByVal id As String)
                MyBase.New(userArgs)
                Me.ID = id
            End Sub
        End Class

    End Class

    ''' <summary>
    ''' Stores the received company statistics data of an asynchronous download.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class CompanyStatisticsDownloadCompletedEventArgs
        Inherits Base.DownloadCompletedEventArgs

        Private mID As String

        ''' <summary>
        ''' Gets the used ID for downloading data.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ID() As String
            Get
                Return mID
            End Get
        End Property
        ''' <summary>
        ''' Gets the response with company statistics data.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Response() As CompanyStatisticResponse
            Get
                Return DirectCast(MyBase.Response, CompanyStatisticResponse)
            End Get
        End Property

        Friend Sub New(ByVal userArgs As Object, ByVal resp As CompanyStatisticResponse, ByVal id As String)
            MyBase.New(userArgs, resp)
            mID = id
        End Sub

    End Class

    ''' <summary>
    ''' Provides information and response of an asynchronous company statistics download.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class CompanyStatisticResponse
        Inherits Base.Response

        ''' <summary>
        ''' Gets the received company statistics data.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Result() As CompanyStatisticsData
            Get
                Return TryCast(MyBase.Result, CompanyStatisticsData)
            End Get
        End Property

        Friend Sub New(ByVal info As Base.ConnectionInfo, ByVal result As CompanyStatisticsData)
            MyBase.New(info, result)
        End Sub

    End Class

End Namespace